//这是我们编写的扫描器主程序，外部的Java的UI界面程序会调用这个主程序，并且获得结果

//调用库，其中pcap.h就是libpcap的头文件
#include <pcap.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include "nettype.h"

//程序推出的几种信号
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGTERM 15

//将网卡实例设为全局的
pcap_t *device = NULL;

// 别人的IP介解析
// void IP_Analysis(ip_header * ip)
// {
//     printf("\n---------IP协议---------\n");
//     printf("版本号:%d\n", ip->ip_version);
//     printf("首部长度:%lu\n", sizeof(ip_header));
//     printf("服务质量:%d\n", ip->ip_tos);
//     printf("总长度:%d\n", ntohs(ip->ip_length));
//     printf("标识:%d\n", ntohs(ip->ip_id));
//     printf("偏移:%d\n", (ip->ip_off & 0x1fff) * 8);
//     printf("生存时间:%d\n", ip->ip_ttl);
//     printf("协议类型:%d\n", ip->ip_protocol);
//     printf("检验和:%d\n", ntohs(ip->ip_checksum));
//     printf("源IP地址:%d.%d.%d.%d\n", ip->saddr.byte1, ip->saddr.byte2, ip->saddr.byte3, ip->saddr.byte4);
//     printf("目的地址:%d.%d.%d.%d\n", ip->daddr.byte1, ip->daddr.byte2, ip->daddr.byte3, ip->daddr.byte4);
// }

//这里分析icmp
void icmp_packet_analyse(const u_char *icmp_packet, char *str_top, int packet_size){
    icmp_header_t icmp_header;

    memcpy(&icmp_header, icmp_packet, sizeof(icmp_header_t));

    sprintf(str_top, "%sicmp类型：%u\n", str_top, icmp_header.type);
    sprintf(str_top, "%sicmp代码：%u\n", str_top, icmp_header.code);
    sprintf(str_top, "%sicmp校验：%u\n", str_top, ntohs(icmp_header.check_sum));
    
}



// 这里分析一下UDP
void udp_packet_analyse(const u_char *udp_packet, char *str_top, int packet_size){
    udp_header_t udp_header;

    memcpy(&udp_header, udp_packet, sizeof(udp_header_t));

    //现在获取源端口和目的端口
    sprintf(str_top, "%sUDP源端口：%u\n", str_top, ntohs(udp_header.src_port));
    sprintf(str_top, "%sUDP目的端口：%u\n", str_top, ntohs(udp_header.dst_port));
    sprintf(str_top, "%sUDP长度：%u\n", str_top, ntohs(udp_header.len));
    sprintf(str_top, "%sUDP校验：%u\n", str_top, ntohs(udp_header.check_sum));
}



//这里是程序的退出回调函数，好处是可以正常执行pcap_close，不影响上网
//但是事实证明这里运行得不好，关闭监听之后还是上不了网
//只能通过重启wifi解决了
void close_sniff()
{

    if (device != NULL)
    {
        printf("程序退出");
        pcap_close(device);
    }
    exit(0);
}

// 这里分析TCP协议
void tcp_packet_analyse(const u_char *tcp_packet, char *str_top, int packet_size)
{
    // 首先创建TCP头
    tcp_header_t tcp_header;

    // 拷贝
    memcpy(&tcp_header, tcp_packet, sizeof(tcp_header_t));

    //现在获取源端口和目的端口
    sprintf(str_top, "%sTCP源端口：%u\n", str_top, ntohs(tcp_header.src_port));
    sprintf(str_top, "%sTCP目的端口：%u\n", str_top, ntohs(tcp_header.dst_port));

    //辨别http包
    u_char *pay_load = tcp_packet + sizeof(tcp_header_t);

    //tcp payload的大小，注意尾部有一个CRC
    int paylaod_len = packet_size - sizeof(ethernet_header_t) - sizeof(ip_header_t) - sizeof(tcp_header_t) - 4;

    // sprintf(str_top, "%s打印TCP包内容：\n", str_top);

    // int i;
    // for (i = 0; i < paylaod_len; ++i)
    // {
    //     //别响！
    //     if (pay_load[i] != '\a')
    //     {
    //         sprintf(str_top, "%s%c", str_top, pay_load[i]);
    //     }

    //     //每50个就换行
    //     if ((i + 1) % 50 == 0)
    //     {
    //         sprintf(str_top, "%s\n", str_top);
    //         // printf("\n");
    //     }
    // }


    // 这里打印http内容
    // int i;
    // int judge = 0;
    // for (i = 0; i < paylaod_len; ++i)
    // {
    //     //x表示以十六进制形式输出。不足两位就补0
    //     // sprintf(str_top, "%s %.2X", str_top, packet[i]);
    //     if(tcp_packet[i] == 'H' && tcp_packet[i+1] == 'T' && tcp_packet[i+2] == 'T' && tcp_packet[i+3] == 'P'){
    //         judge = 1;
    //         printf("下面打印http包头内容\n");
    //     }

    //     if(judge ==  1){
    //         sprintf(str_top, "%s%c", str_top, tcp_packet[i]);
    //         // printf(" %02x", packet[i]);
    //     }
    //     //每16个就换行
    //     // if ((i + 1) % 16 == 0)
    //     // {   
    //     //     sprintf(str_top, "%s\n", str_top);
    //     //     // printf("\n");
    //     // }
    // }

    // printf("%s\n---------------------------\n", str_top);

    //进行字符串匹配，如果匹配到HTTP这几字，那么就是上层协议就是HTTP
    // char *match_str = "HTTP/";
    // 因为TCP是变长的，所以先搜索HTTP的头部
    // sprintf(str_top, "%s\n", str_top);
    // // int i = 0;
    // int min = 200;
    // for (i = 0; i < paylaod_len; i++)
    // {
    //     if (pay_load[i] == 'H' && i < min - 4)
    //     {
    //         if (pay_load[i + 1] == 'T' && pay_load[i + 2] == 'T' && pay_load[i + 3] == 'P' && pay_load[i + 4] == '/')
    //         {
    //             // printf("\a");
    //             // printf("\a");
    //             // printf("\a");
    //             // printf("\a");
    //             // printf("\a");
    //             // printf("\a");


    //             sprintf(str_top, "%sTCP上层协议：http\n", str_top);

    //             sprintf(str_top, "%s打印http内容：%s\n", str_top, pay_load);

    //             //打印http包，一位一位打印
    //             //别响！
    //             int j;
    //             for (j = i; j < paylaod_len; j++)
    //             {
    //                 if (pay_load[i] != '\a')
    //                 {
    //                     sprintf(str_top, "%s%c", str_top, pay_load[i]);
    //                 }

    //                 //每16个就换行
    //                 if ((i + 1) % 50 == 0)
    //                 {
    //                     sprintf(str_top, "%s\n", str_top);
    //                     // printf("\n");
    //                 }
    //             }

    //             break;
    //         }
    //     }
    // }

    // sprintf(str_top, "%s打印内容：%s\n", str_top, pay_load);

    // if (strstr(pay_load, "HTTP/") != NULL)
    // {
    //     sprintf(str_top, "%sTCP上层协议：http，打印内容：%s\n", str_top, pay_load);
    // }
}

//这里分析IP包
void ip_packet_analyse(const u_char *ip_packet, char *str_top, int packet_size)
{
    //IP头
    ip_header_t ip_header;
    //拷贝
    memcpy(&ip_header, ip_packet, sizeof(ip_header_t));

    //我们主要获取三个东西，一个是协议类型，一个是源地址目的地址
    //这里获取源地址和目的地址
    u_int src_ip = ntohl(ip_header.src_ip);

    // 将IP地址放到char类型数组中，然后依次打印
    u_char ip_buf[4];
    // 将源ip拷贝进去
    memcpy(&ip_buf[0], &src_ip, 4 * sizeof(u_char));

    // 然后打印IP地址的每一组

    sprintf(str_top, "%sIP源地址：", str_top);

    int i;
    for (i = 0; i < 4; i++){
        sprintf(str_top, "%s%u", str_top, ip_buf[i]);

        if(i != 3){
            //补点
            sprintf(str_top, "%s.", str_top);
        }else
        {
            sprintf(str_top, "%s\n", str_top);
        }
        
    }

    // 这里打印目的地址
    u_int dest_ip = ntohl(ip_header.dest_ip);
    
    // 将源ip拷贝进去
    memcpy(&ip_buf[0], &dest_ip, 4 * sizeof(u_char));

    // 然后打印IP地址的每一组

    sprintf(str_top, "%sIP目的地址：", str_top);

    
    for (i = 0; i < 4; i++){
        sprintf(str_top, "%s%u", str_top, ip_buf[i]);

        if(i != 3){
            //补点
            sprintf(str_top, "%s.", str_top);
        }else
        {
            sprintf(str_top, "%s\n", str_top);
        }
        
    }

    printf("服务质量:%d\n", ip_header.type_of_service);
    printf("总长度:%d\n", ntohs(ip_header.total_len));
    printf("标识:%d\n", ntohs(ip_header.fragmentation_id));
    printf("生存时间:%d\n", ip_header.TTL);


    //最后获取协议类型
    u_char protocol = ip_header.protocol;

    //查看对应类型的值
    if (protocol == 1)
    {
        //ICMP
        sprintf(str_top, "%sIP上层协议：ICMP\n", str_top);

        icmp_packet_analyse(ip_packet + sizeof(ip_header_t), str_top, packet_size);

        // printf("IP上层协议：ICMP\n");
    }
    else if (protocol == 6)
    {
        // TCP
        sprintf(str_top, "%sIP上层协议：TCP\n", str_top);

        // 这里分析TCP包
        tcp_packet_analyse(ip_packet + sizeof(ip_header_t), str_top, packet_size);
        // printf("IP上层协议：TCP\n");
    }
    else if (protocol == 17)
    {
        //UDP
        sprintf(str_top, "%sIP上层协议：UDP\n", str_top);
        udp_packet_analyse(ip_packet + sizeof(ip_header_t), str_top, packet_size);
        // printf("IP上层协议：UDP\n");
    }
    else
    {
        sprintf(str_top, "%s未知的IP上层协议\n", str_top);
        // printf("未知的IP上层协议\n");
    }
}

//这里用一个函数来分析以太网包payload的内容，首先是链路层，然后是IP，传入链路层数据包和数据包的大小
// 传入一个指针的指针，因为指针可能会被修改，在字符串拼接的时候
// 第三个参数传入包的总大小，就是第二层的大小
void ethernet_packet_analyse(const u_char *packet, char *str_top, int packet_size)
{

    //首先先分析链路层，将链路层搞出来
    ethernet_header_t ethernet_header;

    //将链路层头部拷贝进来
    memcpy(&ethernet_header, packet, sizeof(ethernet_header_t));

    //这里要注意大端小端的问题，长度大于一个字节的数据类型要转一下
    // u_short ntohs(u_short netshort)
    // u_long ntohl(u_long netlong)

    //这里用一个循环来打印目的mac地址
    int i;

    sprintf(str_top, "%s目的MAC地址：", str_top);

    // printf("目的MAC地址：");

    for (i = 0; i < 6; i++)
    {
        //打印16进制，2位对齐，前方补0
        sprintf(str_top, "%s%.2X", str_top, ethernet_header.dhost_mac[i]);
        // printf("%.2X", ethernet_header.dhost_mac[i]);
        if (i != 5)
        {
            sprintf(str_top, "%s:", str_top);
            // printf(":");
        }
    }

    // printf("\n");
    sprintf(str_top, "%s\n", str_top);

    //这里打印源mac地址
    // printf("源MAC地址：");
    sprintf(str_top, "%s源MAC地址：", str_top);

    for (i = 0; i < 6; i++)
    {
        //打印16进制，2位对齐，前方补0
        sprintf(str_top, "%s%.2X", str_top, ethernet_header.shost_mac[i]);
        // printf("%.2X", ethernet_header.shost_mac[i]);
        if (i != 5)
        {
            sprintf(str_top, "%s:", str_top);
            // printf(":");
        }
    }

    sprintf(str_top, "%s\n", str_top);
    // printf("\n");
    //这里打印数据包类型
    u_short kind_of_packet = ntohs(ethernet_header.type);

    // 0800H:IP
    // 0806H:ARP
    // 8035H:RARP
    // printf("%x\n", kind_of_packet);
    if (kind_of_packet == 0x0800)
    {
        //这里将集中分析IP包
        sprintf(str_top, "%s包类型：IP\n", str_top);
        // printf("包类型：IP\n");

        ip_packet_analyse(packet + sizeof(ethernet_header_t), str_top, packet_size);
        // IP_Analysis(packet + sizeof(ethernet_header_t));
    }
    else if (kind_of_packet == 0x0806)
    {
        sprintf(str_top, "%s包类型：ARP\n", str_top);
        // printf("包类型：ARP\n");
    }
    else if (kind_of_packet == 0x0835)
    {
        sprintf(str_top, "%s包类型：RARP\n", str_top);
        // printf("包类型：RARP\n");
    }
    else if (kind_of_packet == 0x86dd)
    {
        sprintf(str_top, "%s包类型：IPv6\n", str_top);
        // printf("包类型：IPv6\n");
    }
    else
    {
        sprintf(str_top, "%s未知包类型，类型编号：%x\n", str_top, kind_of_packet);
        // printf("未知包类型，类型编号：%x\n", kind_of_packet);
    }

    sprintf(str_top, "%s\n", str_top);
    // printf("\n");
}

// 规定包分析结构体
typedef struct
{
    //首先是uchar类型的一个参数，其实是id
    u_char *arg;
    //然后是元数据包
    struct pcap_pkthdr *pkthdr;
    //最后是完整的数据包指针
    u_char *packet;
    //包大小
    int packet_size;

} packet_arg_t;

void *packet_analyse(void *packet_arg)
{

    //打印一下每个报头的大小
    // printf("%d,%d,%d", sizeof(ethernet_header_t), sizeof(ip_header_t), sizeof(tcp_header_t));

    // printf("进入线程\n");
    //首先我么先解析参数
    //传入的是一个指向了选课单结构体的指针
    packet_arg_t *arg_struct = (packet_arg_t *)packet_arg;

    // 然后分别拆出来参数
    u_char *arg = arg_struct->arg;

    const struct pcap_pkthdr *pkthdr = arg_struct->pkthdr;

    const u_char *packet = arg_struct->packet;

    int packet_size = arg_struct->packet_size;

    //处理包的逻辑
    int *id = (int *)arg;

    char *str_top = NULL;

    // 申请打印内容的空间
    str_top = (char *)malloc(70000);

    // 这里拼接位置的offet
    // int offset = 0;

    memset(str_top, 0, 70000);

    // printf("包id: %d\n", ++(*id));
    // printf("包长度: %d\n", pkthdr->len);
    // printf("包字节数: %d\n", pkthdr->caplen);
    // printf("收到的时间: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));

    sprintf(str_top, "%s包id: %d\n", str_top, ++(*id));
    // offset += sprintf(str_top + offset, "包长度: %d\n", pkthdr->len);
    // offset += sprintf(str_top + offset, "包字节数: %d\n", pkthdr->caplen);
    // offset += sprintf(str_top + offset, "收到的时间: %s\n", ctime((const time_t *)&pkthdr->ts.tv_sec));
    sprintf(str_top, "%s包长度: %d\n", str_top, pkthdr->len);
    sprintf(str_top, "%s包字节数: %d\n", str_top, pkthdr->caplen);
    sprintf(str_top, "%s收到的时间: %s\n", str_top, ctime((const time_t *)&pkthdr->ts.tv_sec));

    // printf("包id: %d\n", ++(*id));
    // printf("包长度: %d\n", pkthdr->len);
    // printf("包字节数: %d\n", pkthdr->caplen);
    // printf("收到的时间: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));

    if (pkthdr->len != pkthdr->caplen)
    {
        //数据包不完整，抛弃
        sprintf(str_top, "%s数据包不完整，抛弃\n", str_top);
        // printf("数据包不完整，抛弃\n");

        //打印缓冲区中的内容
        printf("%s\n---------------------------\n", str_top);
        fflush(stdout);

        // pthread_exit(NULL);

        // printf("析构1");
        free(str_top);

        // printf("析构2");
        // 一开始析构掉参数结构体

        free(packet_arg);

        // printf("析构3");
        // 最后要析构掉packet缓冲区

        free(pkthdr);

        // printf("析构4");
        // 析构pkthdr缓冲区

        free(packet);

        return (void *)0;
    }

    //这里打印链路层包头
    ethernet_packet_analyse(packet, str_top, packet_size);

    // printf("123\n");

    //下面是包的内容
    // sprintf(str_top, "%s下面打印包完整内容\n", str_top);
    // printf("下面打印包完整内容\n");
    int i;
    int judge = 0;
    for (i = 0; i < pkthdr->len; ++i)
    {
        //x表示以十六进制形式输出。不足两位就补0
        // sprintf(str_top, "%s %.2X", str_top, packet[i]);
        if(packet[i] == 'H' && packet[i+1] == 'T' && packet[i+2] == 'T' && packet[i+3] == 'P'){
            judge = 1;
            printf("下面打印http包头内容\n");
        }

        if(judge ==  1){
            sprintf(str_top, "%s%c", str_top, packet[i]);
            // printf(" %02x", packet[i]);
        }
        //每16个就换行
        // if ((i + 1) % 16 == 0)
        // {   
        //     sprintf(str_top, "%s\n", str_top);
        //     // printf("\n");
        // }
    }

    printf("%s\n---------------------------\n", str_top);
    fflush(stdout);

    // printf("析构1\n");
    free(str_top);

    // printf("析构2\n");
    // 一开始析构掉参数结构体

    free(packet_arg);

    // printf("析构3\n");
    // 最后要析构掉packet缓冲区

    free(pkthdr);

    // printf("析构4\n");
    // 析构pkthdr缓冲区

    free(packet);

    // pthread_exit(NULL);
    return (void *)0;
}

//这里是用来解析包的函数
//要用字符串拼接的方式整合一个打印函数，只在getPacket中打印一次内容，方便之后的多线程实践，一开始设计一个缓冲区，然后用strcat拼起来
void getPacket(u_char *arg, const struct pcap_pkthdr *pkthdr_old, const u_char *packet_old)
{

    // printf("申请包空间\n");
    // 将packet赶紧拷走
    // 返回之后packet以及pkthdr的指针可能会变，所以先预存起来
    //我们先看看包大概多大
    u_int packet_size = pkthdr_old->caplen;

    u_char *packet = (u_char *)malloc(packet_size);
    // printf("申请pkthdr空间\n");
    struct pcap_pkthdr *pkthdr = (struct pcap_pkthdr *)malloc(sizeof(const struct pcap_pkthdr));

    // printf("初始化\n");
    memset(packet, 0, packet_size);
    memset(pkthdr, 0, sizeof(const struct pcap_pkthdr));

    // printf("拷贝1\n");
    memcpy(packet, packet_old, packet_size);
    // printf("拷贝2\n");
    memcpy(pkthdr, pkthdr_old, sizeof(const struct pcap_pkthdr));

    // 这里进行传参
    // 申请参数结构体
    // printf("申请参数空间\n");
    packet_arg_t *packet_arg = (packet_arg_t *)malloc(sizeof(packet_arg_t));
    memset(packet_arg, 0, sizeof(packet_arg_t));

    // 内容放入
    packet_arg->arg = arg;
    packet_arg->pkthdr = pkthdr;
    packet_arg->packet = packet;
    packet_arg->packet_size = packet_size;

    // 先尝试直接调用

    // printf("创建线程\n");
    // 多线程调用
    pthread_t thread;
    pthread_create(&thread, NULL, &packet_analyse, packet_arg);
    // packet_analyse(packet_arg);

    // printf("退出线程\n");

    // int *id = (int *)arg;

    // char *str_top = NULL;

    // // 申请打印内容的空间
    // str_top = (char *)malloc(70000);

    // // 这里拼接位置的offet
    // // int offset = 0;

    // memset(str_top, 0, 70000);

    // // printf("包id: %d\n", ++(*id));
    // // printf("包长度: %d\n", pkthdr->len);
    // // printf("包字节数: %d\n", pkthdr->caplen);
    // // printf("收到的时间: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));

    // sprintf(str_top, "%s包id: %d\n", str_top, ++(*id));
    // // offset += sprintf(str_top + offset, "包长度: %d\n", pkthdr->len);
    // // offset += sprintf(str_top + offset, "包字节数: %d\n", pkthdr->caplen);
    // // offset += sprintf(str_top + offset, "收到的时间: %s\n", ctime((const time_t *)&pkthdr->ts.tv_sec));
    // sprintf(str_top, "%s包长度: %d\n", str_top, pkthdr->len);
    // sprintf(str_top, "%s包字节数: %d\n", str_top, pkthdr->caplen);
    // sprintf(str_top, "%s收到的时间: %s\n", str_top, ctime((const time_t *)&pkthdr->ts.tv_sec));

    // // printf("包id: %d\n", ++(*id));
    // // printf("包长度: %d\n", pkthdr->len);
    // // printf("包字节数: %d\n", pkthdr->caplen);
    // // printf("收到的时间: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));

    // if (pkthdr->len != pkthdr->caplen)
    // {
    //     //数据包不完整，抛弃
    //     sprintf(str_top, "%s数据包不完整，抛弃\n", str_top);
    //     // printf("数据包不完整，抛弃\n");

    //     //打印缓冲区中的内容
    //     printf("%s\n---------------------------\n", str_top);

    //     return;
    // }

    // //这里打印链路层包头
    // ethernet_packet_analyse(packet, str_top);

    // //下面是包的内容
    // sprintf(str_top, "%s下面打印包完整内容\n", str_top);
    // // printf("下面打印包完整内容\n");
    // int i;
    // for (i = 0; i < pkthdr->len; ++i)
    // {
    //     //x表示以十六进制形式输出。不足两位就补0
    //     sprintf(str_top, "%s %02x", str_top, packet[i]);
    //     // printf(" %02x", packet[i]);

    //     //每16个就换行
    //     if ((i + 1) % 16 == 0)
    //     {
    //         sprintf(str_top, "%s\n", str_top);
    //         // printf("\n");
    //     }
    // }

    // printf("%s\n---------------------------\n", str_top);

    // free(str_top);
}

int main(int argc, char *argv[])
{
    //这里分析命令行参数
    //这里捕获外部参数，用-i来接收
    int opt;
    
    printf("%d,%d,%d\n", sizeof(ethernet_header_t), sizeof(u_int), sizeof(u_long));

    //获取设备名的字符串
    char device_name_buffer[32];
    char *device_name = NULL;
    //这里是过滤器参数
    char filter_str_buffer[1024];
    memset(&filter_str_buffer, 0, 1024);
    char *filter_str = &filter_str_buffer;

    while ((opt = getopt(argc, argv, "i:f:")) != EOF)
    {
        switch (opt)
        {
        case 'i':
            strcpy(&device_name_buffer, optarg);
            device_name = &device_name_buffer;
            printf("确认输入的网卡是%s\n", device_name);
            fflush(stdout);
            break;
        case 'f':
            strcpy(&filter_str_buffer, optarg);
            filter_str = &filter_str_buffer;

            //过滤器参数把空格换成了下划线
            //用一个指针来扫描整个字符串
            char *filter_str_scanner = filter_str;

            while (*filter_str_scanner)
            {
                //遇到下划线就修改为空格
                if (*filter_str_scanner == '_')
                {
                    // printf("找到下划线\n");
                    *filter_str_scanner = ' ';
                }
                ++filter_str_scanner;
            }

            printf("确认过滤器内容%s\n", filter_str);
            fflush(stdout);
            break;
        default:
            printf("other option is wrong\n");
            fflush(stdout);
        }
    }

    //命令行分析参数结束

    //这里要捕获程序退出事件，
    signal(SIGHUP, close_sniff);
    signal(SIGINT, close_sniff);
    signal(SIGQUIT, close_sniff);
    signal(SIGTERM, close_sniff);

    //首先先写一个程序来获取网卡的信息

    // printf("%lu\n", sizeof(int));

    //这里存储获取网卡信息的错误信息
    char errBuf[PCAP_ERRBUF_SIZE];

    //获取网卡名称名称
    // device_name = pcap_lookupdev(errBuf);
    // device_name = "en6";

    if (device_name)
    {
        //在我的机器上，打印出来的是en0，通过ifconfig，可以发现这个是应该这台电脑所连接的无线路由器子网
        printf("成功获取设备信息： %s，请打开对应网口的混杂模式\n", device_name);
        fflush(stdout);
    }
    else
    {
        printf("发生错误： %s\n", errBuf);
        fflush(stdout);
    }

    // device_name = "en6";

    // 进行嗅探，并且等到第一个数据包到来
    //实际上无线网卡要使用的是监听模式！
    // device = pcap_create("en0", errBuf);
    // if (device == NULL)
    // {
    //     printf("创建网络接口实例: %s\n", errBuf);
    //     exit(1);
    // }
    // if (pcap_set_rfmon(device, 1) == 0)
    // {
    //     printf("监测模式打开啦！");
    // }
    // pcap_set_snaplen(device, 65536); // 接受所有数据包
    // pcap_set_promisc(device, 1);     // 打开混杂模式
    // pcap_set_timeout(device, 0);     // 等待数据包
    // int status = pcap_activate(device);

    //这里是有线网口的
    //我们使用混杂模式来打开网络接口
    //下面是这个份接口的总结
    // pcap_t * pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf)
    // 上面这个函数会返回指定接口的pcap_t类型指针，后面的所有操作都要使用这个指针。
    // 第一个参数是第一步获取的网络接口字符串，可以直接使用硬编码。
    // 第二个参数是对于每个数据包，从开头要抓多少个字节，我们可以设置这个值来只抓每个数据包的头部，而不关心具体的内容。典型的以太网帧长度是1518字节，但其他的某些协议的数据包会更长一点，但任何一个协议的一个数据包长度都必然小于65535个字节。
    // 第三个参数指定是否打开混杂模式(Promiscuous Mode)，0表示非混杂模式，任何其他值表示混合模式。如果要打开混杂模式，那么网卡必须也要打开混杂模式，可以使用如下的命令打开eth0混杂模式：
    // ifconfig eth0 promisc
    // ifconfig eth0 -promisc 这行代码用来关闭对应网络接口混杂模式
    // 第四个参数指定需要等待的毫秒数，超过这个数值后，第3步获取数据包的这几个函数就会立即返回。0表示一直等待直到有数据包到来。
    // 第五个参数是存放出错信息的数组。
    pcap_t *device = pcap_open_live(device_name, 65536, 1, 0, errBuf);
    printf("进行嗅探，并且等待第一个数据包到来\n");
    fflush(stdout);

    if (!device)
    {
        printf("error: pcap_open_live(): %s\n", errBuf);
        fflush(stdout);
        exit(1);
    }

    struct bpf_program filter;
    pcap_compile(device, &filter, filter_str, 1, 0);
    pcap_setfilter(device, &filter);

    //第一个数据包到来
    /* wait loop forever */
    int id = 0;

    //这里的这个程序我要合理地退出
    //把getPacket设计为一个异步函数，直接返回，内部开启多线程
    pcap_loop(device, -1, getPacket, (u_char *)&id);

    pcap_close(device);

    return 0;
}
